package com.briup.cloud.framework.logging.utils.resolver;

import com.briup.cloud.framework.logging.service.ILoggingService;
import com.briup.cloud.framework.logging.utils.annotation.LoggingAccess;
import com.briup.cloud.framework.logging.bean.BaseLogging;
import com.briup.cloud.framework.logging.utils.constant.LogTypeEnum;
import com.briup.cloud.framework.logging.utils.holder.LoggingAccessInfoHolder;
import com.briup.framework.utils.common.ClassUtil;
import com.briup.framework.utils.common.NetworkUtil;
import com.briup.framework.utils.execption.BriupFrameworkException;
import com.briup.framework.utils.sercurity.SecurityUtils;
import com.briup.framework.utils.web.AopUtils;
import com.briup.framework.utils.web.SecurityUserDetails;
import com.briup.framework.utils.web.WebUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * 日志注解LoggingAccess的解析类
 */
@Component
@Aspect
@Slf4j
public class LoggingAccessAspectResolver {

    @Autowired
    private ILoggingService loggingService;

    public LoggingAccessAspectResolver(){
        System.out.println("创建日志解析切面。。。。");
    }

    @Pointcut("execution(* com.briup.cloud.framework..web.controller.*.*(..))")
    public void loggingPoincut() {
    }

    @Around("loggingPoincut()")
    public Object loggingAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //System.out.println("是否执行。。。。。。。。");

        BaseLogging loggingAccessInfo = LoggingAccessInfoHolder.initial();

        //设置当前请求时间
        loggingAccessInfo.setRequestTime(new Date());

        MethodSignature methodSignature = ((MethodSignature) proceedingJoinPoint.getSignature());
        Method method = methodSignature.getMethod();

        //获取方法的签名，包含方法名和参数列表
        String methodName = AopUtils.getMethodName(proceedingJoinPoint);

        Object[] args = proceedingJoinPoint.getArgs();

        // target （被代理对象的类型）
        Class<?> target = proceedingJoinPoint.getTarget().getClass();

//        loggingAccessInfo.setDeclaredClassName(target.getName());
//        loggingAccessInfo.setDeclaredMethodName(methodName);
        loggingAccessInfo.setMethod(target.getName()+"."+methodName);

        //设置从LoggingAccess注解上获取的内容新
        //flag为true表示在类上 或者 方法上加了LoggingAccess注解
        boolean flag = loggingAnnotation(loggingAccessInfo, target, method);

        //设置请求中的相关信息
        loggingHeaders(loggingAccessInfo);

        Object results = null;
        try {
            results = proceedingJoinPoint.proceed();
            //设置访问结果为成功
            loggingAccessInfo.setResult(Byte.parseByte("1"));
        } catch (Exception exp) {
            exp.printStackTrace();
            log.error("Illegal argument: {} in {}.{}()", Arrays.toString(args), methodSignature.getDeclaringTypeName(), methodSignature.getName());
            if (exp instanceof BriupFrameworkException) {
                BriupFrameworkException error = (BriupFrameworkException) exp;
                loggingAccessInfo.setCode(error.getCode().getCode());
                loggingAccessInfo.setStatus(error.getCode().getStatus());
                loggingAccessInfo.setMessage(exp.getMessage());
            } else {
                loggingAccessInfo.setStatus("服务器内部错误！");
                loggingAccessInfo.setCode(500);
                loggingAccessInfo.setMessage(exp.getMessage());
            }
            loggingAccessInfo.setResult(Byte.parseByte("0"));
            throw  exp;
        } finally{
            /**设置当前用户的信息*/
            loggingUserDetails(loggingAccessInfo);
            //设置响应时间
            loggingAccessInfo.setResponseTime(new Date());
            //ThreadLocal要有清除操作，避免内存溢出
            LoggingAccessInfoHolder.clear();
            //如果没有 在类上 或者 方法上任何一个地方加入LoggingAccess注解的话，那么就直接返回，不做日志记录
            if(!flag) return results;
            try {
                loggingAccessInfo.setId(UUID.randomUUID().toString());
                loggingAccessInfo.setUsingTime(loggingAccessInfo.getResponseTime().getTime()-loggingAccessInfo.getRequestTime().getTime());
                loggingService.save(loggingAccessInfo);
            } catch (Exception exception) {
                exception.printStackTrace();
                log.error("An exception[{}] occurred while saving the log", exception);
            }
        }
        return results;
    }

    private void loggingHeaders(BaseLogging loggingAccessInfo) {

    	//获取请求对象
        HttpServletRequest request = WebUtil.getHttpServletRequest();

        if (request == null) {
            return;
        }

        //设置请求方式
        loggingAccessInfo.setRequestMethod(request.getMethod());

        //获取并设置请求中基本的头信息
        Map<String, String> headers = WebUtil.getHeaders(request);

        loggingAccessInfo.setRequestUri(request.getRequestURI());//请求相对路径
        loggingAccessInfo.setOriginalIp(NetworkUtil.getIpAddress(request));//ip地址

        String xForwardedHost = headers.get("x-forwarded-host");
        String xForwardedPort = headers.get("x-forwarded-port");
        if (xForwardedHost != null) {
            loggingAccessInfo.setOriginalIp(xForwardedHost.concat(":").concat(xForwardedPort));//ip地址
        }
    }


    private void loggingUserDetails(BaseLogging loggingAccessInfo) {

        SecurityUserDetails user = SecurityUtils.getCurrentUser();

        if (user == null) {
            return;
        }

        if (loggingAccessInfo == null) {
            LoggingAccessInfoHolder.initial();
        }

        if (loggingAccessInfo.getAccountId()!= null &&
                !"".equals(loggingAccessInfo.getAccountId().trim())) {
            return;
        }

        if (loggingAccessInfo.getUsername() != null &&
                !"".equals(loggingAccessInfo.getUsername().trim())) {
            return;
        }

        loggingAccessInfo.setAccountId(user.getAccountId());
        loggingAccessInfo.setUsername(user.getUsername());
    }

    private boolean loggingAnnotation(BaseLogging loggingAccessInfo, Class<?> target, Method method) throws JsonProcessingException {

    	//获取指定方法上的指定类型注解，会自动查找父类中方法
        LoggingAccess methodAnnotation = ClassUtil.getAnnotation(method, LoggingAccess.class);

        //获取指定类上的指定类型注解，会自动查找父类
        LoggingAccess classAnnotation = ClassUtil.getAnnotation(target, LoggingAccess.class);

        if (classAnnotation != null) {
            loggingAccessInfo.setModular(classAnnotation.value());
        }

        //先给一个默认值
        loggingAccessInfo.setLogType(LogTypeEnum.NORMAL.getType());

        if (methodAnnotation != null) {
        	//设置注解中获取内容的，就是将来的日志内容
            loggingAccessInfo.setOperation(methodAnnotation.value());

            //设置日志的类型，登录日志还是普通访问日志
            if (!ObjectUtils.isEmpty(methodAnnotation.type())) {
                loggingAccessInfo.setLogType((byte)methodAnnotation.type());
            }

        }
        return classAnnotation != null || methodAnnotation != null;
    }
}
